﻿#region Copyright(c) 2006 ZO, All right reserved.
// -----------------------------------------------------------------------------
// Copyright(c) 2006 ZO, All right reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   1.  Redistribution of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//   2.  Redistribution in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//   3.  The name of the author may not be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// -----------------------------------------------------------------------------
#endregion


#region Using directives

using System;
using System.Text;
using ZO.SmartCore.Standards.Unicode.Normalization;
using ArgumentNullException= ZO.SmartCore.Core.ArgumentNullException;

#endregion

namespace ZO.SmartCore.Standards.StringPrep
{
    /// <summary>
    /// Base class for all stringprep profile implementations.
    /// </summary>
    /// <remarks>
    /// Implementation must override prepare to apply their specific rules. 
    /// This usually involves calling the following methods (in order)
    /// </remarks>
    public abstract class StringPrepProfile
    {

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="StringPrepProfile"/> class with the given parameters.
        /// </summary>
        /// <param name="unassigned">true if strings should be checked for unassigned codepoints.</param>
        /// <param name="normalize">true if strings are normalized.</param>
        /// <param name="mappings">The array of MappingTables to apply.</param>
        /// <param name="prohibited">The array of ProhibitedTables to apply.</param>
        /// <remarks>
        ///  If mappings is null, prepared strings are not mapped.
        ///  if prohibited is null, prepared strings are not verified against any prohibited characters.
        /// </remarks>
        protected StringPrepProfile(bool unassigned,
                            bool normalize,
                            MappingTable[] mappings,
                            ProhibitedTable[] prohibited)
        {
            _Unassigned = (unassigned ? Unassigned : null);
            _Normalize = normalize;
            _Mappings = ((mappings != null) ? mappings : null);
            _Prohibited = ((prohibited != null) ? prohibited : null);
        }
        #endregion

        #region Destructor

        #endregion

        #region Fields
        private UnassignedTable _Unassigned;
        private bool _Normalize;
        private MappingTable[] _Mappings;
        private ProhibitedTable[] _Prohibited;

        #endregion

        #region Events

        #endregion

        #region Operators

        #endregion

        #region Properties
        /// <summary>
        /// Bidirectional Characters
        /// </summary>
        public static readonly BidirectionalTable BIDI = BidirectionalTable.Create(RFC3454.RFC3454_C_8, RFC3454.RFC3454_D_1, RFC3454.RFC3454_D_2);
        /// <summary>
        /// Table of unassigned characters. This table is defined in RFC 3454, Appendix A.1.
        /// </summary>
        public static readonly UnassignedTable Unassigned = UnassignedTable.Create(RFC3454.RFC3454_A_1);
        /// <summary>
        /// Mapping of characters-to-nothing. This table is defined in RFC 3454, Appendix B.1.
        /// </summary>
        public static readonly MappingTable MappingB1 = MappingTable.Create(RFC3454.RFC3454_B_1_From, RFC3454.RFC3454_B_1_To);

        /// <summary>
        /// Mapping for case-folding over NFKC. This table is defined in RFC 3454, Appendix B.2.
        /// </summary>
        public static readonly MappingTable MappingB2 = MappingTable.Create(RFC3454.RFC3454_B_2_From, RFC3454.RFC3454_B_2_To);

        /// <summary>
        /// Mapping for case-folding not over NFKC. This table is defined in RFC 3454, Appendix B.3.
        /// </summary>
        public static readonly MappingTable MappingB3 = MappingTable.Create(RFC3454.RFC3454_B_3_From, RFC3454.RFC3454_B_3_To);

        /// <summary>
        /// Table of prohibited ASCII space characters. This table is defined in RFC 3454, Appendix C.1.1.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC11 = ProhibitedTable.Create(RFC3454.RFC3454_C_1_1);


        /// <summary>
        /// Table of prohibited non-ASCII space characters. This table is defined in RFC 3454, Appendix C.1.2.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC12 = ProhibitedTable.Create(RFC3454.RFC3454_C_1_2);

        /// <summary>
        /// Table of prohibited ASCII control characters. This table is defined in RFC 3454, Appendix C.2.1.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC21 = ProhibitedTable.Create(RFC3454.RFC3454_C_2_1);


        /// <summary>
        /// Table of prohibited non-ASCII control characters. This table is defined in RFC 3454, Appendix C.2.2.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC22 = ProhibitedTable.Create(RFC3454.RFC3454_C_2_2);


        /// <summary>
        /// Table of prohibited private use characters. This table is defined in RFC 3454, Appendix C.3.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC3 = ProhibitedTable.Create(RFC3454.RFC3454_C_3);


        /// <summary>
        /// Table of prohibited non-character code points. This table is defined in RFC 3454, Appendix C.4.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC4 = ProhibitedTable.Create(RFC3454.RFC3454_C_4);


        /// <summary>
        /// Table of prohibited surrogate codes. This table is defined in RFC 3454, Appendix C.5.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC5 = ProhibitedTable.Create(RFC3454.RFC3454_C_5);


        /// <summary>
        /// Table of prohibited characters inappropriate for plain text. This table is defined in RFC 3454, Appendix C.6.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC6 = ProhibitedTable.Create(RFC3454.RFC3454_C_6);

        /// <summary>
        /// Table of prohibited characters inappropriate for canonical representation. This table is defined in RFC 3454, Appendix C.7.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC7 = ProhibitedTable.Create(RFC3454.RFC3454_C_7);

        /// <summary>
        /// Table of prohibited characters that change display properties or are deprecated. This table is defined in RFC 3454, Appendix C.8.
        /// </summary>
        public static readonly ProhibitedTable ProhibitedC8 = ProhibitedTable.Create(RFC3454.RFC3454_C_8);

        #endregion

        #region Methods
        /// <summary>
        /// Prepares a string using this profile.
        /// </summary>
        /// <param name="text">The string to prepare, contains prepared text on return.</param>
        /// <remarks>The value returned by this method is never null.</remarks>
        public void Prepare(StringBuilder text)
        {

            if ((text == null) || (text.Length == 0))
                throw new ArgumentNullException("text");

            IsUnassigned(text);
            text = Map(text);
            text = Normalize(text);
            IsProhibited(text);
            IsBidirectional(text);
        } // prepare

        /// <summary>
        /// Prepares a string using this profile.
        /// </summary>
        /// <param name="text">The string to prepare.</param>
        /// <returns>The prepared string.</returns>
        /// <remarks>The value returned by this method is never null.</remarks>
        public string Prepare(String text)
        {

            if (String.IsNullOrEmpty(text))
                throw new ArgumentNullException("text");

            StringBuilder buffer = new StringBuilder(text);
            this.Prepare(buffer);
            return buffer.ToString();

        } // prepare


        /// <summary>
        /// Verifies bidirectionality of the input.
        /// </summary>
        /// <param name="buffer">The string buffer to verify.</param>
        protected static void IsBidirectional(StringBuilder buffer)
        {
            BIDI.Verify(buffer);
        } // IsBidirectional

        /// <summary>
        /// Verifies input does not contain unassigned codepoints.
        /// </summary>
        /// <param name="buffer">The string buffer to verify.</param>
        protected void IsUnassigned(StringBuilder buffer)
        {

            if (this._Unassigned != null)
                this._Unassigned.Verify(buffer);

        } // Unassign

        /// <summary>
        /// Maps the input.
        /// </summary>
        /// <param name="buffer">The string buffer to map.</param>
        /// <returns><tt>buffer</tt>, mapped.</returns>
        protected StringBuilder Map(StringBuilder buffer)
        {

            if (this._Mappings != null)
            {
                for (int idx = 0; idx < this._Mappings.Length; idx++)
                    this._Mappings[idx].Map(buffer);
            }
            return buffer;
        }

        /// <summary>
        /// Normalizes the input using Form KC.
        /// </summary>
        /// <param name="text">The string buffer to normalize.</param>
        /// <returns>The normalized string buffer.</returns>
        protected StringBuilder Normalize(StringBuilder text)
        {
            if (this._Normalize)
            {
                NFKC.Normalize(text);
            }
            return text;
        } // Normalize

        /// <summary>
        /// Verifies input does not contain prohibited codepoints.
        /// </summary>
        /// <param name="buffer">The string buffer to verify.</param>
        /// <exception cref="ProhibitedException">Throws If there are any errors while verifying <tt>buffer</tt></exception>
        protected void IsProhibited(StringBuilder buffer)
        {
            if (this._Prohibited != null)
            {
                for (int idx = 0; idx < this._Prohibited.Length; idx++)
                {
                    this._Prohibited[idx].Verify(buffer);
                }
            }

        } // prohibited
        #endregion
    }
}
